package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.SocketException;

import utils.Configuration;

import network.config.NetworkConfiguration;
import network.config.NetworkConfiguration.PortType;


/**
 * UDPSender class, used to send packet on unicast or broadcast.
 */
public class UDPSender {
	
	private NetworkConfiguration networkConfig ;
	private DatagramSocket socket ;
	
	/**
	 * Create a new UDPSender that can be used to send User Signal on
	 * netowkr.
	 * 
	 * @param netconf The network configuration
	 * @throws SocketException if an error occurs creating the socket
	 */
	public UDPSender (NetworkConfiguration netconf) throws SocketException {
		this.networkConfig = netconf ;
		this.socket = new DatagramSocket() ;
	}
	
	/**
	 * Send a signal according to UserSignal specified an current network configuration.
	 * 
	 * @param aus The user signal containing information about receiver (address) and packet,
	 * if address in aus is null, the signal is send on broadcast.
	 * @throws SendSignalException if an error occurs sending the signal (only for unicast)
	 */
	public void sendSignal (AbstractUserSignal aus) throws SendSignalException {
		/* Serialize the signal, and throw an exception if it's null. */
		byte[] bytes = aus.serialize() ;
		if (bytes == null) {
			throw new SendSignalException ("Unable to serialize signal.") ;
		}
		if (aus.getAddress() != null) {
			/* No broadcast, create a simple DatagramPacket and send it to the specified host.
			 * Throw SendSignalException if an error occurs.  */
			try {
				DatagramPacket packet = new DatagramPacket(bytes, bytes.length, 
						new InetSocketAddress(aus.getAddress(), this.networkConfig.getPort(PortType.UDP_SEND)));
				Configuration.net_debug.println("Sending " + aus + " to " + packet.getSocketAddress()) ;
				this.socket.send(packet) ;
			} 
			catch (SocketException e) {
				throw new SendSignalException (e) ;
			} 
			catch (IOException e) {
				throw new SendSignalException (e) ;
			}
		}
		else {
			/* Broadcast, for each allowed interface, create and send a DatagramPacket.
			 * Do not throw exception if an error occurs. */
			for (InterfaceAddress interf : this.networkConfig.getAllowedInterfaces()) {
				try {
					DatagramPacket packet = new DatagramPacket(bytes, bytes.length, 
							new InetSocketAddress(interf.getBroadcast(), this.networkConfig.getPort(PortType.UDP_SEND)));
					Configuration.net_debug.println("Sending " + aus + " to " + packet.getSocketAddress()) ;
					this.socket.send(packet) ;
				}
				catch (SocketException e) {
				} 
				catch (IOException e) {
				}
			}
		}
	}

}
